#include "i2c_soft_drv.h"

/**
 * @desc  : I2C软实现初始化
 * @date  : 2024-11-21
 * @author: AnRui
 */
void I2cSoftDrvInit(void) {
    rcu_periph_clock_enable(I2C_SCL_RCU);
    gpio_init(I2C_SCL_PORT, GPIO_MODE_OUT_OD, GPIO_OSPEED_10MHZ, I2C_SCL_PIN);
    rcu_periph_clock_enable(I2C_SDA_RCU);
    gpio_init(I2C_SDA_PORT, GPIO_MODE_OUT_OD, GPIO_OSPEED_10MHZ, I2C_SDA_PIN);
}


static void I2cSoftSendStop(void);

/**
 * @desc  : 主设备向从设备发送开始信号
*  @brief    SCL为高电平时，SDA由高电平向低电平跳变，开始传输数据
*           生成下图所示的波形图，即为起始时序
*                1 2    3     4
*                 _____________
*           SCL :              \_____
*                 ________
*           SDA :         \___________
 * @date  : 2024-11-19
 * @author: AnRui
 */
static void I2cSoftStart(void) {
    SDA(1);
    SCL(1);
    I2C_DELAY();

    SDA(0);
    I2C_DELAY();

    SCL(0);
    I2C_DELAY();
}

/**
* @desc  : 发送数据
*                1 2     3      4
*                         ______
*           SCL: ________/      \______
*                ______________________
*           SDA: \\\___________________
 *
 * @date  : 2024-11-19
 * @author: AnRui
 * @param :  data - {参数说明}:
 */
static void I2cSoftSendData(uint8_t data) {
    uint8_t i;
    for (i = 0;i < 8;i++) {
        //发送方法主要集中在这 每次根据最高位来发 下面依次把低位移到高位上去
        if (data & 0x80) {
            SDA(1);
        } else {
            SDA(0);
        }
        I2C_DELAY();
        SCL(1);
        I2C_DELAY();
        SCL(0);
        I2C_DELAY();
        data <<= 1;
    }
}


/**
 * @desc  : 等待响应
 *
 *             ____
 *            |    |
 *  SCL ______|    |_______
 *         ___       ______
 *        |   *     |
 *  SDA __|   ******|
 *
 *  注意: *位置代码从设备拉低
 * @date  : 2024-11-19
 * @author: AnRui
 */
static I2cStatusCode_e I2cSoftWaitAck(void) {
    SET_SDA_MODE_IN(); // SDA交出控制权，SDA变为浮空输入  会自动把SDA置为高电平
    I2C_DELAY();       // 等待从机拉低SDA 

    SCL(1);
    I2C_DELAY();
    uint8_t retry = RETRY_TIMES;
    while (GET_SDA_PIN_STATE() == SET && retry > 0) {
        retry--;
        I2C_DELAY();
    }
    SCL(0);
    SET_SDA_MODE_OUT();
    if (retry <= 0) { return I2C_ERR_SLAVE_NO_ACK; }
    I2C_DELAY();
    return I2C_RESULT_OK;
}

/**
 * @desc  : 接收数据
 *          ______
 *         |      |
 *  SCL ___|      |___
 *        ___________
 *       |           |
 *  SDL  |           |  BIT1
 *
 *       |           |
 *  SDL  |           |  BIT0
 *        ———————————
 *
 * @date  : 2024-11-19
 * @author: AnRui
 * @return: uint8_t
 */
static uint8_t I2cSoftResv(void) {
    SET_SDA_MODE_IN();
    I2C_DELAY();
    // SDA应该交给从设备，主设备应该是输入状态
    uint8_t i, data;
    data = 0;
    for (i = 0; i < 8; i++) {
        SCL(1);// 开始设置数据有效性
        I2C_DELAY();
        data <<= 1;
        data |= GET_SDA_PIN_STATE();
        // data |= GET_SDA_PIN_STATE() << (7 - i);
        SCL(0);// 给机会从设备准备数据
        I2C_DELAY();
    }
    SET_SDA_MODE_OUT();
    I2C_DELAY();
    return data;
}


/**
 * @desc  : 主机发送ACK响应
 *           ______
 *          |      |
 *  SCL  ___|      |___
 *
 *      |
 *  SDA |
 *       ———————————————
 *
 *
 * @date  : 2024-11-19
 * @author: AnRui
 */
static void I2cSoftSendAck(void) {
    // 拉低SDA
    SDA(0);
    I2C_DELAY();
    SCL(1);
    I2C_DELAY();
    SCL(0);
    I2C_DELAY();

}

/**
 * @desc  : 发送空响应
 *
 *           ______
 *          |      |
 *  SCL  ___|      |___
 *
 *       ———————————————
 *      |
 *  SDA |
 *
 * @date  : 2024-11-19
 * @author: AnRui
 */
static void I2cSoftSendNack(void) {
    // 拉高SDA
    SDA(1);
    I2C_DELAY();

    // 拉高SCL
    SCL(1);
    I2C_DELAY();
    // 拉低SCL
    SCL(0);
    I2C_DELAY();
}



/**
 * @desc  : 主设备向从设备发送结束信号
 *        _________
 *       |
 *  SCL _|
 *           ______
 *          |
 *  SDL ____|
 *
 * @date  : 2024-11-19
 * @author: AnRui
 */
static void I2cSoftSendStop(void) {
    SDA(0);
    I2C_DELAY();
    SCL(1);
    I2C_DELAY();
    SDA(1);
    I2C_DELAY();
}

/**
 * @desc  : 等待从机准备完毕
 * @date  : 2025-04-25
 * @author: AnRui
 * @param : addr - {参数说明}: 从机地址 注意不是写地址或读地址 是地址
 * @param : delayTimes - {参数说明}: 等待次数
 * @return: I2cStatusCode_e
 * @note  : 等待从机准备完毕 例如Eeprom 每写一页数据 都需要等待一段时间 最大5ms
 *          等待次数*60us=最长等待时间 ->例如 delayTimes=100 100*60us=6ms
 */
I2cStatusCode_e WaitDeviceIsReady(uint8_t addr, uint32_t delayTimes) {
    I2cStatusCode_e res = I2C_RESULT_OK;
    while (delayTimes) {
        // 开始
        I2cSoftStart();
        // 发送设备地址
        I2cSoftSendData(addr << 1);
        // 从机是否响应 每I2cSoftWaitAck执行的时间大概为: (RETRY_TIMES+2)*I2C_DELAY=60us
        res = I2cSoftWaitAck();
        I2cSoftSendStop();
        if (res == I2C_RESULT_OK) { break; }
        delayTimes--;
    }
    return res;
}

/**
 * @desc  : I2C 写数据
 * @date  : 2024-11-20
 * @author: AnRui
 * @param :  addr -{设备地址 7位}: 不是写地址或读地址
 * @param :  reg  -{寄存器地址}:
 * @param :  data -{保存数据的数组的指针}:
 * @param :  len  -{数组长度}:
 * @return: 0:正常  1 代表发送设备地址错误  2 代表发送寄存器地址错误 3 代表发送数据错误
 */
I2cStatusCode_e I2cSoftWrite(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len) {
    I2cStatusCode_e i2cStatusCode = I2C_RESULT_OK;
    // 开始
    I2cSoftStart();
    // 发送设备地址
    I2cSoftSendData(addr << 1);
    // 等待ack
    if (I2C_RESULT_OK != I2cSoftWaitAck()) {
        i2cStatusCode = I2C_ERR_DEVICE_ADDR;
        goto i2c_err;
    }
    // 发送寄存器地址
    I2cSoftSendData(reg);
    // 等待ack
    if (I2C_RESULT_OK != I2cSoftWaitAck()) {
        i2cStatusCode = I2C_ERR_REGISTER_ADDR;
        goto i2c_err;
    }

    for (uint32_t i = 0;i < len; i++) {
        I2cSoftSendData(data[i]);
        if (I2C_RESULT_OK != I2cSoftWaitAck()) {
            i2cStatusCode = I2C_ERR_WRITE;
            goto i2c_err;
        }
    }
    I2cSoftSendStop();
    return i2cStatusCode;
i2c_err:
    I2cSoftSendStop();
    return i2cStatusCode;
}


/**
 * @desc  : 读数据
 * @date  : 2024-11-19
 * @author: AnRui
 * @param :  addr -{设备地址 7位}:
 * @param :  reg  -{寄存器地址}:
 * @param :  data -{保存数据的数组的指针}:
 * @param :  len  -{数组长度}:
 * @return: 0:正常  1 代表发送设备地址错误  2 代表发送寄存器地址错误 3 代表发送数据错误
 */
I2cStatusCode_e I2cSoftRead(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len) {
    I2cStatusCode_e i2cStatusCode = I2C_RESULT_OK;
    I2cSoftStart();

    // 发送设备地址
    I2cSoftSendData(addr << 1);
    // 等待ack
    if (I2C_RESULT_OK != I2cSoftWaitAck()) {
        i2cStatusCode = I2C_ERR_DEVICE_ADDR;
        goto i2c_err;
    }

    // 发送寄存器地址
    I2cSoftSendData(reg);
    // 等待ack
    if (I2C_RESULT_OK != I2cSoftWaitAck()) {
        i2cStatusCode = I2C_ERR_REGISTER_ADDR;
        goto i2c_err;
    }
    I2cSoftStart();
    // 发送设备地址(读)
    I2cSoftSendData((addr << 1) | 1);

    // 等待ack
    if (I2C_RESULT_OK != I2cSoftWaitAck()) {
        i2cStatusCode = I2C_ERR_DEVICE_ADDR;
        goto i2c_err;

    }

    uint32_t i;
    for (i = 0; i < len; i++) {
        // 接收数据
        data[i] = I2cSoftResv();
        if (i == len - 1) {
            // 如果是最后一条，发送NACK
            I2cSoftSendNack();
        } else {
            // 如果不是最后一条，发送ACK
            I2cSoftSendAck();
        }
    }

    I2cSoftSendStop();
    return i2cStatusCode;
i2c_err:
    I2cSoftSendStop();
    return i2cStatusCode;
}
